// DMPluginWrapper.h

#pragma once

#include "dmalgointerfaces.h"


using namespace System;
using namespace System::Reflection;
using namespace System::Collections;
using namespace System::Runtime::InteropServices;
using namespace System::Diagnostics;
#include "DMEnumerationWrappers.h"
#include "DMMiningParameter.h"
#include "DMCaseIterHelper.h"
#include "DMAttributeSet.h"

namespace Microsoft
{
namespace SqlServer
{
namespace DataMining
{
namespace PluginAlgorithms
{
	
	// Externals
	ref class AttributeGroup ;
	
	// Pre-definitions
	ref class PushCaseSet;
	ref class InternalCaseProcessor;
	ref class MiningCase;
	ref class StateValue;
	ref class PredictionResult;
	ref class AttStatsBinaryHeap;
	ref class AttStatsScorePair;
	interface class ICaseProcessor;




	///////////////////////////////////////////////////////////
	// Push case set -- wrapper class for IDMPushCaseSet
	public   ref class PushCaseSet sealed 
	{
		IDMPushCaseSet*	m_wpCaseSet;

	internal:
		PushCaseSet(IDMPushCaseSet*	in_wpCaseSet)
		{
			m_wpCaseSet	=	in_wpCaseSet;
		}

	
	public:
		System::Boolean		IsDense();
	
		void				StartCases(ICaseProcessor ^ caseProcessor);
	};


///////////////////////////////////////////////////////////
// MiningCase -- Allows access to attribute states
	
	[DefaultMember("Item")]
	public   ref class MiningCase sealed 
	{
		internal:
			DMCaseItr*	m_wpCaseItr;

			MiningCase()
			{
				m_wpCaseItr = NULL;
			}

			void SetCaseIterator(DMCaseItr*	pCaseItr)
			{
				m_wpCaseItr = pCaseItr;
			}
	
		public:
			bool	MoveNext();
			bool	MoveFirst();


			// returns the current attribute --in place => fast
			

			property System::UInt32 Attribute
			{
				System::UInt32 get();
			}

			// returns the current state index or double val --in place => fast
			property System::UInt32 IndexValue
			{
				System::UInt32 get();
			}

			
			property System::Double DoubleValue
			{
				System::Double get();
			}


			
			property StateValue ^ Value
			{
				StateValue ^ get();
			}

			
			
			[System::Diagnostics::CodeAnalysis::SuppressMessage("Microsoft.Design", "CA1043:UseIntegralOrStringArgumentForIndexers")]
			property StateValue^	Item[System::UInt32]
			{
				StateValue^ get(System::UInt32 att)
				{
					return FindGetValue(att);
				}
				
			}

			StateValue^		FindGetValue(System::UInt32 attribute);

	};


    
	///////////////////////////////////////////////////////////
	// CaseProcessorBase -- Implements IDCaseProcessor
	ref class InternalCaseProcessor : 
                            public ICLR_DMCaseProcessor
	{
	private:
		bool					m_fIsDense;
		IDMContextServices*		m_wpEC;
		MiningCase		^		m_trainingCase;
		ICaseProcessor	^		m_caseProcessor;
	
	internal:
		void	SetDense(bool in_fDense)
		{
			m_fIsDense = in_fDense;
		}

		void SetExecutionContext(IDMContextServices* wpEC)
		{
			m_wpEC	=	wpEC;
		}


	internal:
		InternalCaseProcessor(ICaseProcessor^ caseProcessor)
		{
			m_trainingCase = gcnew MiningCase();
			m_fIsDense = false;
			m_caseProcessor	=	caseProcessor;
		}

		

	public:
		virtual HRESULT ExportedRequestCapacity(
					ULONG in_cCapacity) sealed;

		virtual HRESULT ExportedProcessCase(
					DM_CaseID in_CaseID,
					ULONG	in_cAttributes ,
					ULONG*	in_rgValues) sealed;
	protected:
		virtual void	ProcessCase( System::Int64 caseId, MiningCase ^ inputCase){};


	};



	
	
							

	///////////////////////////////////////////////////////////
	// Prediction Result -- contains the prediction input flags 
	// on input and wraps the prediction result on output
	public   ref class PredictionResult sealed 
	{
	private:
		
		// System::Collections::ArrayList	^	m_mrgAttributeDistributions;
		AttStatsBinaryHeap^					m_mHeapAttributeDistributions;
		System::Collections::ArrayList	^	m_mrgOutputAttributes;

		
	
	internal:
		DM_PREDICTION_FLAGS		m_flags;
		IDMAttributeGroup*		m_wpAttrGroup;
		ULONG					m_ulMaxPredictions;
		ULONG					m_ulMaxStates;

		DM_ATTRIBUTE_STAT**		m_ppPredictions;
		ULONG*					m_pcPredictions;

		bool					m_fPreAlloc;
		bool					m_fStatesAlloc;

		DM_ATTRIBUTE_VALUE*		m_rgInputAttValues;
		ULONG					m_ulInputAttValues;
		
		PredictionResult(	DM_PREDICTION_FLAGS	in_Flags, 
							IDMAttributeGroup*	in_wpAttributeGroup,
							ULONG				in_ulMaxPredictions,
							ULONG				in_ulMaxStates,
							DM_ATTRIBUTE_VALUE*	in_rgInputAttValues,
							ULONG				in_ulInputAttValues);

		HRESULT	SetOutputBuffers(DM_ATTRIBUTE_STAT** in_prgPredictions, ULONG* in_pcPredictions)
		{
			m_ppPredictions	=	in_prgPredictions;
			m_pcPredictions	=	in_pcPredictions;

			m_fPreAlloc		=	(*m_ppPredictions != NULL);

			if( m_fPreAlloc && (m_ulMaxPredictions == DM_UNSPECIFIED) )
			{
				// todo: bogdanc --  Raise error -- inconsistent input
				return E_INVALIDARG;
			}

			return S_OK;
		}

		void ErrorCleanup();

		// Formats the results:
		// Allocates  (or preserves) the output buffers
		// Selects the MAX ATTs and MAX STATEs
		// Filters out the statistics if necessary
		// Updates the result buffers
		void PostProcess();

	public:
		// MUST be taken into consideration, there is no way PredictionResult can filter this out after 
		
		
		property System::Boolean IncludeMissingState
		{
			System::Boolean get();
		}
		
		// MAY be ignored
		
		property System::Boolean IncludeStatistics
		{
			System::Boolean get();
		}
		
		// MUST be taken into consideration
		

		property System::Boolean IncludeNodeId
		{
			System::Boolean get();
		}

		/*orig. managed*/ enum class InputAttributesTreatment
		{
			PredictOnlyInputAttributes,
			ExcludeInputAttributes,
			None,
		};
		// MAY be ignored 
		

		property InputAttributesTreatment InputAttributeRestrictions
		{
			InputAttributesTreatment get();
		}




		/*orig. managed*/ enum class SortDirection
		{
			Ascending,
			Descending
		};
		// MAY be ignored
		
		
		property SortDirection AttributeSortDirection
		{
			SortDirection get();
		}
		// MAY be ignored
		

		property SortDirection StateSortDirection
		{
			SortDirection get();
		}

		
		
		
		/*orig. managed*/ enum class SortBy
		{
			None,
			Support,
			Probability,
			Variance,
			AdjustedProbability,
		};
		// MAY be ignored
		

		property SortBy AttributeSortBy
		{
			SortBy get();
		}
		// MAY be ignored
		

		property SortBy StateSortBy
		{
			SortBy get();
		}



		// MAY be ignored
		

		property System::UInt32 MaxPredictions
		{
			System::UInt32 get()
			{
				return m_ulMaxPredictions;
			}
		}
		
		// MAY be ignored
		
		property System::UInt32 MaxStates
		{
			System::UInt32 get()
			{
				return m_ulMaxStates;
			}
		}
		

		property AttributeGroup ^	OutputAttributes
		{
			AttributeGroup ^	get();
		}

		void AddPrediction( AttributeStatistics ^ attStat );
		
	};

	private  value class StatisticsComparer sealed  : public IComparer
	{
		PredictionResult::SortBy	m_criterion;

		public:
			StatisticsComparer (PredictionResult::SortBy criterion)
			{
				m_criterion = criterion;
			}

			virtual int Compare( Object ^ att1, Object ^ att2 );
	};


	/// <summary>
	/// AttStats Score pair implementation. 
	/// </summary>
	ref class AttStatsScorePair sealed
	{
	private:
		AttributeStatistics ^		m_attStats;
		double						m_dblScore;
		PredictionResult::SortBy	m_sortBy;

	public:
		AttStatsScorePair(PredictionResult::SortBy	sortBy, double initialScore)
		{
			m_sortBy	=	sortBy;
			m_attStats	=	nullptr;
			m_dblScore	=	initialScore;
		}

		AttStatsScorePair(PredictionResult::SortBy	sortBy, AttributeStatistics^	attStats)
		{
			m_sortBy	=	sortBy;
			m_attStats	=	attStats;

			m_dblScore	=	System::Convert::ToDouble(attStats->Attribute);
			switch( m_sortBy )
			{
				case PredictionResult::SortBy::Support:
					m_dblScore	=	attStats->Support;
					break;
				case PredictionResult::SortBy::Probability:
					m_dblScore	=	attStats->Probability;
					break;
				case PredictionResult::SortBy::AdjustedProbability:
					m_dblScore	=	attStats->AdjustedProbability;
					break;
				// Variance is not supported for attribute statistics
				case PredictionResult::SortBy::Variance:
				case PredictionResult::SortBy::None:
				default:
					m_dblScore	=	System::Convert::ToDouble(attStats->Attribute);
			}
		}

		property AttributeStatistics^	AttStats
		{
			AttributeStatistics^ get()
			{
				return m_attStats;
			}
		}

		property double Score
		{
			double get()
			{
				return m_dblScore;
			}
		}

		static bool operator < (AttStatsScorePair^ asp1, AttStatsScorePair^ asp2)
		{
			return asp1->Score < asp2->Score;
		}
		static bool operator > (AttStatsScorePair^ asp1, AttStatsScorePair^ asp2)
		{
			return asp1->Score > asp2->Score;
		}
		static bool operator <= (AttStatsScorePair^ asp1, AttStatsScorePair^ asp2)
		{
			return asp1->Score <= asp2->Score;
		}
		static bool operator >= (AttStatsScorePair^ asp1, AttStatsScorePair^ asp2)
		{
			return asp1->Score >= asp2->Score;
		}

	};


	/// <summary>
	/// AttributeStats Binary Heap implementation
	/// </summary>
	ref class AttStatsBinaryHeap sealed 
	{
	private:
		long 						m_Size;
		long 						m_maxSize;
		bool						m_fMaximumHeap;
		array<AttStatsScorePair^>^	m_H;
		PredictionResult::SortBy	m_sortBy;

	public:
		/// <summary>
		/// Constructor
		/// </summary>
		AttStatsBinaryHeap( long in_maxSize, PredictionResult::SortBy sortBy, PredictionResult::SortDirection sortDirection)
		{
			m_Size     = 0;
			m_maxSize  = in_maxSize;

			m_fMaximumHeap = sortDirection == PredictionResult::SortDirection::Descending;

			m_H = gcnew  array<AttStatsScorePair^>(m_maxSize+1);
			m_sortBy = sortBy;
			
			//create empty heap top
			if( m_fMaximumHeap )
				m_H[0] = gcnew AttStatsScorePair(sortBy, System::Double::MinValue);
			else
				m_H[0] = gcnew AttStatsScorePair(sortBy, System::Double::MaxValue);
		}

		/// <summary>
		/// Insert an element
		/// </summary>
		void Insert(AttributeStatistics^ in_attStat)
		{
			int i = -1;
			Debug::Assert(m_maxSize > 0);
			AttStatsScorePair^	 insertPair = gcnew AttStatsScorePair(m_sortBy, in_attStat);
			
			if (m_Size >= m_maxSize)
			{
				OverwriteRoot(insertPair);
				return;
			}

			m_Size++;
			i = m_Size;
			if (m_fMaximumHeap)
			{
				while (m_H[i / 2] > insertPair)
				{
					m_H[i] = m_H[i / 2];
					i = i / 2;
				}
			}
			else
			{
				while (m_H[i / 2] < insertPair)
				{
					m_H[i] = m_H[i / 2];
					i = i / 2;
				}
			}
			m_H[i] = insertPair;
		}


		/// <summary>
		/// Delete the minimum element, and return it.
		/// </summary>
		AttributeStatistics^	DeleteRoot()
		{
			int i = 1;
			int child = 1;
			Debug::Assert(m_maxSize > 0);

			AttStatsScorePair^ last = nullptr;
			AttStatsScorePair^ prevRoot = nullptr;
			
			if (m_Size == 0)
			{
				return nullptr;
			}
			last = m_H[m_Size];
			Debug::Assert(m_Size > 0);
			prevRoot  = m_H[1];
			m_Size--;

			while (true)
			{
				//----------------------------------------------
				// Find smaller/larger child
				//----------------------------------------------
				child = i * 2;
				if (child > m_Size)
					break;
				if (m_fMaximumHeap)
				{
					if (child != m_Size && m_H[child] > m_H[child + 1])
						child++;
				}
				else
				{
					if (child != m_Size && m_H[child] < m_H[child + 1])
						child++;
				}

				//----------------------------------------------
				// Push down one level if needed
				//----------------------------------------------
				if (m_fMaximumHeap)
				{
					if (last <= m_H[child])
						break;
				}
				else
				{
					if (last >= m_H[child])
						break;
				}
				m_H[i] = m_H[child];
				i = child;
			}

			m_H[i] = last;
			m_H[m_Size + 1] = nullptr;

			return prevRoot->AttStats;
		}			



		/// <summary>
		/// Return the size of the Heap.
		/// </summary>
		property int Length
		{
			int get()
			{
				Debug::Assert(m_maxSize > 0);
				return(m_Size);
			}
		}
	private:
		/// <summary>
		/// Overwrite the root element.  Then percolate down. 
		/// </summary>
		void OverwriteRoot(AttStatsScorePair^ in_asp)
		{
			int i = 1;
			int child = 1;
			
			Debug::Assert(m_maxSize > 0);
			Debug::Assert(m_Size > 0);
			if (m_Size == 0)
				return;

			if (m_fMaximumHeap)
			{
				if (m_H[1] >= in_asp)
					return;
			}
			else
			{
				if (m_H[1] <= in_asp)
				return;
			}

			m_H[1] = in_asp;
			AttStatsScorePair^ first  = m_H[1];
			while (true)
			{
				//----------------------------------------------
				// Find smaller/larger child
				//----------------------------------------------
				child = i * 2;
				if (child > m_Size)
					break;
				if (m_fMaximumHeap)
				{
					if (child != m_Size && m_H[child] > m_H[child + 1])
						child++;
				}
				else
				{
					if (child != m_Size && m_H[child] < m_H[child + 1])
						child++;
				}

				//----------------------------------------------
				// Push down one level if needed
				//----------------------------------------------
				if (m_fMaximumHeap)
				{
					if (first <= m_H[child])
						break;
				}
				else
				{
					if (first >= m_H[child])
						break;
				}

				m_H[i] = m_H[child];
				i      = child;
			}
			m_H[i] = first;
		}			


	};

	
	public ref class ClusterMembershipInfo sealed
	{
	private:
		System::UInt64	m_clusterID;
		System::String^	m_caption;
		System::String^	m_nodeUniqueName;
		double			m_distance;
		double			m_membership;
	public:
		ClusterMembershipInfo()
		{
			m_clusterID = (System::UInt64)-1;
			m_caption = String::Empty;
			m_nodeUniqueName = String::Empty;
			m_distance = 1.0;
			m_membership  = 0.0;
		}

		property System::UInt64 ClusterId
		{
			System::UInt64 get()
			{
				return m_clusterID;
			}
			void set(System::UInt64 value)
			{
				m_clusterID = value;
			}
		}
		property System::String^ Caption
		{
			System::String^ get()
			{
				return m_caption;
			}
			void set(System::String^ value)
			{
				m_caption = value;
			}
		}
		property System::String^ NodeUniqueName
		{
			System::String^ get()
			{
				return m_nodeUniqueName;
			}
			void set(System::String^ value)
			{
				m_nodeUniqueName = value;
			}
		}

		property System::Double Distance
		{
			System::Double get()
			{
				return m_distance;
			}
			void set(System::Double value)
			{
				m_distance = value;
			}
		}

		property System::Double Membership
		{
			System::Double get()
			{
				return m_membership;
			}
			void set(System::Double value)
			{
				m_membership = value;
			}
		}
	};

	///////////////////////////////////////////////////////////
	// case processor
	public interface class ICaseProcessor
	{
	public:
		virtual void	ProcessCase( System::Int64 caseId, MiningCase ^ inputCase);
	};

}
}
}
}
